﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CloudAppMonitor.Properties;
using CloudAppMonitor.Data.EntityData;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace CloudAppMonitor.Data
{
    internal class SqlStorage
    {
        Settings _settings;
        IDictionary<string, int> _deployments;
        IDictionary<string, int> _roles;
        IDictionary<string, int> _roleInstances;
        IDictionary<string, int> _counters;
        IDictionary<string, int> _channels;
        CloudAppMonitorDbDataContext _db;

        public SqlStorage(Settings settings)
        {
            _settings = settings;

            _db = new CloudAppMonitorDbDataContext(
                string.Format("Data Source={0};Integrated Security=True;Initial Catalog={1}",
                    settings.DatabaseServer,
                    settings.DatabaseName));

            _counters = _db.Counters.ToDictionary(k => k.CounterName, v => v.CounterId);
            _roleInstances = _db.RoleInstances.ToDictionary(k => k.RoleInstanceName, v => v.RoleInstanceId);
            _roles = _db.Roles.ToDictionary(k => k.RoleName, v => v.RoleId);
            _deployments = _db.Deployments.ToDictionary(k => k.DeploymentName, v => v.DeploymentId);
            _channels = _db.Channels.ToDictionary(k => k.ChannelName, v => v.ChannelId);
        }

        internal void StoreEntity(TableStorageEntity entity)
        {
            if (entity is PerformanceCountersTableEntity)
            {
                StorePerformanceCounter((PerformanceCountersTableEntity)entity);
            }
            else if (entity is WindowsEventLogsTableEntity)
            {
                StoreWindowsEventLog((WindowsEventLogsTableEntity)entity);
            }
            else if (entity is WindowsAzureDiagnosticLogs)
            {
                StoreWindowsAzureLog((WindowsAzureDiagnosticLogs)entity);
            }
        }

        private void StoreWindowsEventLog(WindowsEventLogsTableEntity windowsEventLog)
        {
            DateTime eventTime = new DateTime(windowsEventLog.EventTickCount);

            WindowsLogEntry windowsLogEntry = (from wel in _db.WindowsLogEntries
                                               where wel.ChannelId == GetChannelId(windowsEventLog) &&
                                                     wel.EventTime == eventTime &&
                                                     wel.RoleInstanceId == GetRoleInstanceId(windowsEventLog)
                                               select wel).FirstOrDefault();

            if (windowsLogEntry == null)
            {
                windowsLogEntry = new WindowsLogEntry();
                windowsLogEntry.ChannelId = GetChannelId(windowsEventLog);
                windowsLogEntry.EventTime = eventTime;
                windowsLogEntry.RoleInstanceId = GetRoleInstanceId(windowsEventLog);
                windowsLogEntry.EventId = windowsEventLog.EventId;
                windowsLogEntry.Level = windowsEventLog.Level;
                windowsLogEntry.LogXml = windowsEventLog.RawXml;
                windowsLogEntry.Pid = windowsEventLog.Pid;
                windowsLogEntry.ProviderGuid = windowsEventLog.ProviderGuid;
                windowsLogEntry.ProviderName = windowsEventLog.ProviderName;
                windowsLogEntry.Tid = windowsEventLog.Tid;

                _db.WindowsLogEntries.InsertOnSubmit(windowsLogEntry);
            }
            else
            {
                windowsLogEntry.EventId = windowsEventLog.EventId;
                windowsLogEntry.Level = windowsEventLog.Level;
                windowsLogEntry.LogXml = windowsEventLog.RawXml;
                windowsLogEntry.Pid = windowsEventLog.Pid;
                windowsLogEntry.ProviderGuid = windowsEventLog.ProviderGuid;
                windowsLogEntry.ProviderName = windowsEventLog.ProviderName;
                windowsLogEntry.Tid = windowsEventLog.Tid;
            }

            _db.SubmitChanges();
        }

        private void StorePerformanceCounter(PerformanceCountersTableEntity performanceCounter)
        {
            DateTime eventTime = new DateTime(performanceCounter.EventTickCount);

            CounterValue counterValue = (from cv in _db.CounterValues
                                         where cv.CounterId == GetCounterId(performanceCounter) &&
                                               cv.EventTime == eventTime &&
                                               cv.RoleInstanceId == GetRoleInstanceId(performanceCounter)
                                         select cv).FirstOrDefault();

            if (counterValue == null)
            {
                counterValue = new CounterValue();
                counterValue.CounterId = GetCounterId(performanceCounter);
                counterValue.EventTime = eventTime;
                counterValue.RoleInstanceId = GetRoleInstanceId(performanceCounter);
                counterValue.Value = performanceCounter.CounterValue;

                _db.CounterValues.InsertOnSubmit(counterValue);
            }
            else
            {
                counterValue.Value = performanceCounter.CounterValue;
            }

            _db.SubmitChanges();
        }

        private void StoreWindowsAzureLog(WindowsAzureDiagnosticLogs azureLog)
        {
            DateTime eventTime = new DateTime(azureLog.EventTickCount);

            AzureLogEntry azureLogEntry = (from al in _db.AzureLogEntries
                                           where al.EventTime == eventTime &&
                                                 al.RoleInstanceId == GetRoleInstanceId(azureLog)
                                           select al).FirstOrDefault();

            if (azureLogEntry == null)
            {
                azureLogEntry = new AzureLogEntry();
                azureLogEntry.EventTime = eventTime;
                azureLogEntry.RoleInstanceId = GetRoleInstanceId(azureLog);
                azureLogEntry.EventId = azureLog.EventId;
                azureLogEntry.Level = azureLog.Level;
                azureLogEntry.Message = azureLog.Message;
                azureLogEntry.Pid = azureLog.Pid;
                azureLogEntry.Tid = azureLog.Tid;

                _db.AzureLogEntries.InsertOnSubmit(azureLogEntry);
            }
            else
            {
                azureLogEntry.EventTime = eventTime;
                azureLogEntry.RoleInstanceId = GetRoleInstanceId(azureLog);
                azureLogEntry.EventId = azureLog.EventId;
                azureLogEntry.Level = azureLog.Level;
                azureLogEntry.Message = azureLog.Message;
                azureLogEntry.Pid = azureLog.Pid;
                azureLogEntry.Tid = azureLog.Tid;
            }

            _db.SubmitChanges();
        }

        private int GetChannelId(WindowsEventLogsTableEntity windowsEventLogEntry)
        {
            if (!_channels.ContainsKey(windowsEventLogEntry.Channel))
            {
                Channel newChannel = new Channel();
                newChannel.ChannelName = windowsEventLogEntry.Channel;

                _db.Channels.InsertOnSubmit(newChannel);
                _db.SubmitChanges();

                _channels = _db.Channels.ToDictionary(k => k.ChannelName, v => v.ChannelId);
            }

            return _channels[windowsEventLogEntry.Channel];
        }

        private int GetCounterId(PerformanceCountersTableEntity performanceCounter)
        {
            if (!_counters.ContainsKey(performanceCounter.CounterName))
            {
                Counter newCounter = new Counter();
                newCounter.CounterName = performanceCounter.CounterName;

                _db.Counters.InsertOnSubmit(newCounter);
                _db.SubmitChanges();

                _counters = _db.Counters.ToDictionary(k => k.CounterName, v => v.CounterId);
            }

            return _counters[performanceCounter.CounterName];
        }

        private int GetRoleInstanceId(WindowsAzureDiagnosticBase performanceCounter)
        {
            if (!_roleInstances.ContainsKey(performanceCounter.RoleInstance))
            {
                RoleInstance newRoleInstance = new RoleInstance();
                newRoleInstance.RoleId = GetRoleId(performanceCounter);
                newRoleInstance.RoleInstanceName = performanceCounter.RoleInstance;

                _db.RoleInstances.InsertOnSubmit(newRoleInstance);
                _db.SubmitChanges();

                _roleInstances = _db.RoleInstances.ToDictionary(k => k.RoleInstanceName, v => v.RoleInstanceId);
            }

            return _roleInstances[performanceCounter.RoleInstance];
        }

        private int GetRoleId(WindowsAzureDiagnosticBase performanceCounter)
        {
            if (!_roles.ContainsKey(performanceCounter.Role))
            {
                Role newRole = new Role();
                newRole.DeploymentId = GetDeploymentId(performanceCounter);
                newRole.RoleName = performanceCounter.Role;

                _db.Roles.InsertOnSubmit(newRole);
                _db.SubmitChanges();

                _roles = _db.Roles.ToDictionary(k => k.RoleName, v => v.RoleId);
            }

            return _roles[performanceCounter.Role];
        }

        private int GetDeploymentId(WindowsAzureDiagnosticBase performanceCounter)
        {
            if (!_deployments.ContainsKey(performanceCounter.DeploymentId))
            {
                Deployment newDeployment = new Deployment();
                newDeployment.DeploymentName = performanceCounter.DeploymentId;

                _db.Deployments.InsertOnSubmit(newDeployment);
                _db.SubmitChanges();

                _deployments = _db.Deployments.ToDictionary(k => k.DeploymentName, v => v.DeploymentId);
            }

            return _deployments[performanceCounter.DeploymentId];
        }

        public IEnumerable<Deployment> Deployments
        {
            get
            {
                return _db.Deployments;
            }
        }

        public IEnumerable<Role> Roles
        {
            get
            {
                return _db.Roles;
            }
        }

        public IEnumerable<RoleInstance> RoleInstances
        {
            get
            {
                return _db.RoleInstances;
            }
        }

        public IEnumerable<Counter> Counters
        {
            get
            {
                return _db.Counters;
            }
        }

        public IEnumerable<Channel> Channels
        {
            get
            {
                return _db.Channels;
            }
        }

        internal DateTime? OldestCounter(IEnumerable<Counter> counters, IEnumerable<RoleInstance> roleInstances)
        {
            if (counters.Any() && roleInstances.Any())
            {
                var counterIds = from c in counters
                                 select c.CounterId;

                var roleInstanceIds = from ri in roleInstances
                                      select ri.RoleInstanceId;

                return (from cv in _db.CounterValues
                        where counterIds.Contains(cv.CounterId) && roleInstanceIds.Contains(cv.RoleInstanceId)
                        select cv.EventTime).Min();
            }

            return null;
        }

        internal DateTime? NewestCounter(IEnumerable<Counter> counters, IEnumerable<RoleInstance> roleInstances)
        {
            if (counters.Any() && roleInstances.Any())
            {
                var counterIds = from c in counters
                                 select c.CounterId;

                var roleInstanceIds = from ri in roleInstances
                                      select ri.RoleInstanceId;

                return (from cv in _db.CounterValues
                        where counterIds.Contains(cv.CounterId) && roleInstanceIds.Contains(cv.RoleInstanceId)
                        select cv.EventTime).Max();
            }

            return null;
        }

        internal IEnumerable<CounterValue> GetCounterValues(DateTime startDate,
            DateTime endDate,
            IEnumerable<Counter> counters,
            IEnumerable<RoleInstance> roleInstances)
        {
            var counterIds = from c in counters
                             select c.CounterId;

            var roleInstanceIds = from ri in roleInstances
                                  select ri.RoleInstanceId;

            return (from cv in _db.CounterValues
                    where counterIds.Contains(cv.CounterId) &&
                          roleInstanceIds.Contains(cv.RoleInstanceId) &&
                          cv.EventTime >= startDate &&
                          cv.EventTime <= endDate
                    select cv).ToList();
        }

        internal IEnumerable<WindowsLogEntry> GetWindowsEventLogEntries(RoleInstance roleInstance, Channel channel)
        {
            return from wel in _db.WindowsLogEntries
                   where wel.Channel == channel && wel.RoleInstance == roleInstance
                   orderby wel.EventTime descending
                   select wel;
        }

        internal IEnumerable<AzureLogEntry> GetAzureLogEntries(RoleInstance roleInstance)
        {
            return from al in _db.AzureLogEntries
                   where al.RoleInstance == roleInstance
                   orderby al.EventTime descending
                   select al;
        }

        internal void DeleteEntry(WindowsLogEntry windowsLogEntry)
        {
            _db.WindowsLogEntries.DeleteOnSubmit(windowsLogEntry);
            _db.SubmitChanges();
        }

        internal void DeleteEntry(AzureLogEntry azureLogEntry)
        {
            _db.AzureLogEntries.DeleteOnSubmit(azureLogEntry);
            _db.SubmitChanges();
        }

        internal object GetCountersForRoleInstance(int roleInstanceId)
        {
            return (from cv in _db.CounterValues
                    join c in _db.Counters on cv.CounterId equals c.CounterId
                    where cv.RoleInstanceId == roleInstanceId
                    select c).Distinct();
        }
    }
}
